package com.imooc.netty.websocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

/**
 * 设定channel的初始化器
 * 在netty服务器server中设置channel后,启动的时候会自动执行下面的初始化方法
 * 泛型SocketChannel理解为是固定的即可
 * @author liansen
 * @create 07-15-0:05
 */
public class WSServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {

        //通过SocketChannel获取对应的管道, 所有的助手类都放到pipeline中去
        ChannelPipeline pipeline = channel.pipeline();

        /*
         * 通过管道添加handler, websocket协议基于http协议,所以要有http编解码器
         */
        //HttpServerCodec是netty自己提供的助手类,可以理解为拦截器
        //作用是当请求到netty服务器，需要做解码，响应到客户端做编码
        pipeline.addLast("HttpServerCodec", new HttpServerCodec());

        //对写大数据流的支持
        pipeline.addLast(new ChunkedWriteHandler());

        //对httpMessage进行聚合，聚合成FullHttpRequest或者FullHttpResponse
        //几乎在netty编程中都会用到此handler
        pipeline.addLast(new HttpObjectAggregator(1024*64));

        //=============================以上是用于http协议==================================

        //=============================增加心跳机制-start=============================

        /*
         * 针对客户端，如果10分钟时没有向服务端发送读写心跳(All), 则主动断开
         * 如果是读空闲或者写空闲, 不处理
         * 默认的时间单位是秒。若需要指定其他时间单位，可以使用四个参数的构造方法
         * readerIdleTimeSeconds: 读超时时间, 即测试端一定时间内未接收到被测试端消息
         * writerIdleTimeSeconds: 写超时时间, 即测试端一定时间内向被测试端发送消息
         * allIdleTimeSeconds:  所有类型的超时时间
         * 这段代码的意思是:
         * 在服务器端会每隔10分钟来检查一下channelRead方法被调用的情况，
         * 如果在10分钟内该链上的channelRead(源码)方法都没有被触发，就会调用userEventTriggered方法(在自定义handler中实现了)。
         *
         *  IdleStateHandler这个类会根据你设置的超时参数的类型和值，循环去检测channelRead和write方法多久没有被调用了，
         * 如果超时了，则会调用userEventTriggered方法，且会告诉你超时的类型
         * 如果没有超时，则会循环定时检测，除非你将IdleStateHandler移除Pipeline
         */
        pipeline.addLast(new IdleStateHandler(800,1000,6000, TimeUnit.SECONDS));
        //自定义的空闲状态检测
        pipeline.addLast(new HeartBeatHandler());

        //=============================增加心跳机制-end=============================

        //=============================以下是用于websocket协议==================================

        /*
         * websocket 服务器处理的协议，用于指定给客户端连接访问的路由: /ws
         * 本handler会帮你处理一些繁重的事
         * 会帮你处理握手动作，handshaking(close, ping, pong) ping+pong = 心跳
         * 对于websocket来讲，都是以frames进行传输，不同的数据类型对应的frames不同
         * chatServerUrl: "ws://192.168.31.106:875/ws", 中最后的/ws就是后端设置的路由
         */
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        //自定义的handler
        pipeline.addLast(new WSHandler());
    }
}
